# FileSystemObserver - Uses pyinotify to detect file system changes.
# Copyright (C) 2007 Lauri Taimila
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"

import os
from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent

from backend.components.mediacache.image_cache_inotify_handler import ImageCacheInotifyHandler
from backend.components.mediacache.music_cache_inotify_handler import MusicCacheInotifyHandler
from backend.components.mediacache.video_cache_inotify_handler import VideoCacheInotifyHandler

class FileSystemObserver(object):
    """
    Detects changes in filesystem and updated cache accordingly.
    
    This class uses pyinotify to detect file system changes in the content
    directories. For example, when user removes image file from the directory
    that is listed in content.conf, we remove that file from cache. This class
    provides such a mechanism that we can efficiently keep our content cache
    up-to-date all the time.
    """

    # Mask - Determines which changes in filesystem we are interested in
    MASK = EventsCodes.IN_DELETE | EventsCodes.IN_CREATE | EventsCodes.IN_DELETE_SELF | EventsCodes.IN_MODIFY | EventsCodes.IN_MOVED_FROM | EventsCodes.IN_MOVED_TO | EventsCodes.IN_MOVE_SELF
      
    def __init__(self, logger, configuration):
        """
        Create a new FileSystemObserver object.
        @param logger: Logger object
        @param configuration: Configuration object
        """
        self.wdd = {} # Watches
        self.wm = WatchManager() # Watch manager object
        self.notifier = ThreadedNotifier(self.wm, None) # Notifier Thread (this receives notifications and calls methods accordingly)
        self.logger = logger
        self.configuration = configuration
    
    def addFolder(self, path, cache_type):
        """
        Add folder (and it subfolders) to observed folders.
        @param path: Path as string
        @param cache_type: Cache type string. Possible values: images, music, video
        """
        if not os.path.exists(path):
            self.logger.warning("Path '" + path + "' doesn't exists. Not observed.")
            return
        
        if cache_type == "image":
            handler = ImageCacheInotifyHandler(self.logger, self.configuration, self)
        elif cache_type == "music":
            handler = MusicCacheInotifyHandler(self.logger, self.configuration, self)
        elif cache_type == "video":
            handler = VideoCacheInotifyHandler(self.logger, self.configuration, self)
            
        self.wdd = self.wm.add_watch(path, self.__class__.MASK, rec=True,  proc_fun=handler)
        self.logger.debug("Observing folder: " + str(path))
            
    def removeFolder(self, path):
        """
        Remove folder (and it subfolders) from observed folders.
        @param path: Remove this path from observed paths. (String)
        """
        if self.wdd.has_key(path) and self.wdd[path] > 0:
            self.wm.rm_watch(self.wdd[path], rec=True)
            self.logger.debug("FileSystemObserver.removeFolder:" + str(path))
    
    def getObservedFolders(self):
        """
        Return list of folders that are observed. 
        Also all subfolders are observed implicitely, but not listed here.
        """
        return self.wdd.keys()
    
    def startObserving(self):
        """
        Start to observe file system changes.
        """
        self.notifier.start()
        
    def stopObserving(self):
        """
        Stop observing file system changes.
        """
        self.notifier.stop()
